let express      = require('express'),
    bodyParser   = require('body-parser'),
    cookieParser = require('cookie-parser')
    path         = require("path"),
    fs           = require('fs')

class Credit {
  // constructor
  constructor(props) {
    // express实例
    this.app = express()
    // 服务
    this.server = null
    // 运行端口
    this.port = 3000

    /* 状态
     * waitTerminalTask      : 等待终端发送任务
     * waitPluginRequestTask : 已经从终端收到任务,等待插件下一次请求任务
     * waitPluginPromise     : 已经把任务返回给了插件,等待插件反馈是否接收任务
     * pluginResolve         : 插件已经接收任务,并开始执行任务
     * pluginReject          : 插件拒绝接收任务
     * pluginDone            : 插件任务已经完成
     * pluginFail            : 插件任务失败
     * returnToTerminal      : 已经反馈任务结果给终端
     * timeout               : 任务超时
     * 
     */
    this.status = 'waitTerminalTask'
    this.legalStatusList = ['waitTerminalTask', 'waitPluginRequestTask', 'waitPluginPromise', 'pluginResolve', 'pluginReject', 'pluginDone', 'pluginFail', 'returnToTerminal', 'timeout']

    // 任务队列 
    this.taskQueue = []
    // 任务结果
    this.taskResult = null
    // 任务id
    this.taskStamp = null

    // 任务步骤超时计时器
    this.timeoutTimer = null
    // 计时器间隔
    this.timeoutTimerSpeed = 1 * 1000
    // 计时器超时时间
    this.timeoutTimerLimit = 20 * 1000
    // 计时器
    this.timeoutTimerIndex = 0

    // 用户请求的IP
    // 根据IP判断做任务权限管理
    // 一次只能一个用户操作
    this.user = null

    // 是否接受新用户的任务
    this.isLock = false

    // 锁定任务超时计时器
    this.lockTimeoutTimer = null
    // 锁定任务超时计时器超时时间
    this.lockTimeoutTimerLimit = 30 * 1000
    
    // 接口
    this.interface = {
      // 插件的接口
      plugin: {
        // 请求任务
        requestTask: '/remote/requestTask',
        // 接受并开始执行任务
        resolveTask: '/remote/resolveTask',
        // 拒绝接受任务
        rejectTask: '/remote/rejectTask',
        // 任务执行成功
        doneTask: '/remote/doneTask',
        // 任务执行失败
        failTask: '/remote/failTask'
      },
      // 终端的接口
      terminal: {
        // 发送任务
        sendTask: '/terminal/task'
      }
      
    }
    
    // 任务清单,终端发来的任务只能是此中之一
    this.legalTaskList = [
      /* ========== 页面跳转 ========== */
      // 跳转到首页
      '2index',
      // 跳转到登录页面
      '2login',
      // 跳转到注册页面
      '2reg',
      // 跳转到找回密码页面
      '2findPassword',
      // 跳转到登录后的欢迎页
      '2welcome',
      // 跳转到申请报告页面
      '2applicationReport',
      // 跳转到获取报告页面
      '2getReport',
      // 退出
      'quit',
      
      /* ========== 获取页面元素 ========== */
      // 获取登录页图片验证码,base64格式
      'getLoginValidImage',
      // 获取注册页图片验证码,base64格式
      'getRegStep1ValidImage',
      // 获取注册步骤二手机验证码
      'getStep2ValidCode',
      // 获取信用报告手机验证码
      'getReportValidCode',
      // 申请问卷
      'applicationReport',
      // 获取
      'getReport',

      /* ========== 填充表单 ========== */
      'inputLoginForm',
      // 填写问卷
      'inputQues',
      // 填写注册步骤一表单
      'inputRegStep1Form',
      // 填写注册步骤二表单
      'inputRegStep2Form',
      // 填写征信报告验证码表单
      'inputReportForm'
    ]

    this.init()
  }

  init() {
    this.parse()
    this.receive()
    this.runServer()
  }
  
  // 解析
  parse() {
    this.parsePost()
    this.parseCrossDomain()
    this.parseStatic()
    this.parseCookie()
  }
  
  // 解析post请求
  parsePost() {
    this.app.use(bodyParser.urlencoded({ extended: true }))
    this.app.use(bodyParser.json())
    console.log('解析post请求')
  }

  // 解析cookie
  parseCookie() {
    this.app.use(cookieParser())

    console.log('解析cookie')
  }
  
  // 设置跨域权限
  parseCrossDomain() {
    this.app.use(function(req, res, next) {
      res.header('Access-Control-Allow-Origin', '*')
      res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
      next()
    })
    console.log('设置跨域权限')
  }

  // 解析静态资源
  parseStatic() {
    this.app.use(express.static(path.join(__dirname, "./public/terminal/dist")))
    console.log('设置静态资源地址')
  }
  
  // 捕获请求
  receive() {
    this.receivePlugin()
    this.receiveTerminal()
  }
  
  // 捕获插件的消息
  receivePlugin() {
    this._receivePluginRequestTask()
    this._receivePluginResolveTask()
    this._receivePluginRejectTask()
    this._receivePluginDoneTask()
    this._receivePluginFailTask()
  }
  
  // 捕获插件发来的请求任务消息
  _receivePluginRequestTask() {
    let that = this
    
    // 插件请求任务啦
    this.app
        .route(that.interface.plugin.requestTask)
        .get(function(req, res, next) {
          // 判断任务队列是否存在任务如果存在则把任务返回给插件
          if (that.isEmptyArray(that.taskQueue)) {
            res.send({
              status: false,
              message: '没有任务'
            })

            return
          }

          // 只能在状态是waitPluginRequestTask的时候才能访问此接口
          if (that.status !== 'waitPluginRequestTask') {
            res.send({
              status: false,
              message: `当前状态 ${ that.status } 不允许访问此接口`
            })

            return
          }

          res.send({
            status: true,
            data: that.taskQueue[that.taskQueue.length - 1]
          })

          // 改变状态
          that.status = 'waitPluginPromise'
        })

    console.log('捕获插件发来的请求任务消息')
  }
  
  // 捕获插件发来的接受任务消息
  _receivePluginResolveTask() {
    let that = this

    this.app
        .route(that.interface.plugin.resolveTask)
        .get(function(req, res, next) {
          // 只能在状态是waitPluginPromise的时候才能访问此接口
          if (that.status !== 'waitPluginPromise') {
            res.send({
              status: false,
              message: `当前状态 ${ that.status } 不允许访问此接口`
            })

            return
          }

          res.send(req.query)

          // 改变状态
          that.status = 'pluginResolve'

        })

    console.log('捕获插件发来的接受任务消息')
  }
  
  // 捕获插件发来的拒绝任务消息
  _receivePluginRejectTask() {
    let that = this

    this.app
        .route(that.interface.plugin.rejectTask)
        .get(function(req, res, next) {
          
          // 只能在状态是waitPluginPromise的时候才能访问此接口
          if (that.status !== 'waitPluginPromise') {
            res.send({
              status: false,
              message: `当前状态 ${ that.status } 不允许访问此接口`
            })

            return
          }

          res.send(req.query)

          // 改变状态
          that.status = 'pluginReject'
          that.taskResult = req.query
        })

    console.log('捕获插件发来的拒绝任务消息')
  }
  
  // 捕获插件发来的任务完成消息
  _receivePluginDoneTask() {
    let that = this

    this.app
        .route(that.interface.plugin.doneTask)
        .get(function(req, res, next) {

          // 只能在状态是pluginResolve的时候才能访问此接口
          if (that.status !== 'pluginResolve') {
            res.send({
              status: false,
              message: `当前状态 ${ that.status } 不允许访问此接口`
            })

            return
          }

          res.send(req.query)

          that.log(req.body)

          // 改变状态
          that.status = 'pluginDone'
          that.taskResult = req.query
        })

    console.log('捕获插件发来的任务完成消息')
  }
  
  // 捕获插件发来的任务失败消息
  _receivePluginFailTask() {
    let that = this

    this.app
        .route(that.interface.plugin.failTask)
        .get(function(req, res, next) {
          // 只能在状态是pluginResolve的时候才能访问此接口
          if (that.status !== 'pluginResolve') {
            res.send({
              status: false,
              message: `当前状态 ${ that.status } 不允许访问此接口`
            })

            return
          }

          res.send(req.query)

          // 改变状态
          that.status = 'pluginFail'
          that.taskResult = req.query

          if (req.query.task === 'quit') {
            that.resetTask()
          }
        })

    console.log('捕获插件发来的任务失败消息')
  }

  // 捕获终端的消息
  receiveTerminal() {
    this._receiveTerminalSendTask()
  }
  
  // 捕获终端发来的发布任务消息
  _receiveTerminalSendTask() {
    let that = this

    this.app
        .route(that.interface.terminal.sendTask)
        .post(function(req, res, next) {

          console.log(req.body)

          // 每次请求任务的保存用户的IP
          // 判断是否和上次的IP是否相同
          // 如果相同可以请求
          // 不同则等待上一个IP任务释放或者超时
          // 如果是老人在任务完成后的一定时间内不操作将释放控制
          // 获取用户IP req.connection.remoteAddress

          let user = req.cookies.stamp

          if (!user) {
            res.cookie('stamp', new Date())
          }

          if (user !== that.user && that.isLock) { // 如果是新用户且已被锁定则不可以请求任务
            res.send({
              status: false,
              message: '服务器繁忙'
            })

            return
          }

          that.user = user

          /*
           * 当接受到终端发来任务的时需要做以下几个判断
           * 1.接口参数是否正确
           *   1) 是否是空对象
           *   2) task是否存在
           *   3) stamp是否存
           *   4) stamp是否是合法格式
           * 2.判断task是否在任务列表中
           * 3.判断当前状态是否可以请求任务
           * 4.如果参数正常则保存在队列,并开启超时计时器,等待下次插件请求任务时返回给插件
           *
           */

          // 判断参数空对象
          if (that.isEmptyObject(req.body)) {
            res.send({
              status: false,
              message: '参数为空'
            })

            return
          }

          // 判断task是否存在
          if (!req.body.task) {
            res.send({
              status: false,
              message: 'task参数错误'
            })

            return
          }

          // 判断stamp是否存在
          if (!req.body.stamp) {
            res.send({
              status: false,
              message: 'stamp参数错误'
            })

            return
          }

          // 判断stamp是否是合法格式
          if (!that.isPositiveInteger(req.body.stamp)) {
            res.send({
              status: false,
              message: 'stamp格式错误'
            })

            return
          }

          // 判断task是否在任务列表中中
          if (!that.isInTaskList(req.body.task)) {
            res.send({
              status: false,
              message: 'task不是合法的任务'
            })

            return
          }

          // 判断当前状态是否可以发布任务
          if (that.status !== 'waitTerminalTask') {
            res.send({
              status: false,
              message: '服务器繁忙'
            })

            return
          }
          
          that.status = 'waitPluginRequestTask'
          that.taskQueue.push(req.body)
          that.taskStamp = req.body.stamp

          that.log(req.body)

          that.runTimeoutTimer(that.status, function() {

            if (that.status === 'pluginReject' || that.status === 'pluginFail') {
              res.send(that.taskResult)

              that.stopTimeoutTimer(function() {
                that.resetTask()
              })
            }
            
            // 任务成功
            if (that.status === 'pluginDone') {
              res.send(that.taskResult)

              that.stopTimeoutTimer(function() {
                that.resetTask()
              })

              // 开启任务锁定,锁定20秒
              switch (req.body.task) {
                case 'applicationReport':
                  // 申请问卷页面 10 分钟
                  that.runLockTimeoutTimer(10 * 60 * 1000)
                  break
                case 'inputLoginForm':
                  // 登录界面
                  that.runLockTimeoutTimer(50 * 1000)
                  break
                case 'inputRegStep1Form':
                  // 注册一界面 2 分钟
                  that.runLockTimeoutTimer(2 * 60 * 1000)
                  break
                case 'inputRegStep2Form':
                  // 注册二界面 2 分钟
                  that.runLockTimeoutTimer(2 * 60 * 1000)
                  break
                default:
                  that.runLockTimeoutTimer()
              }
            }

          }, function() {
            res.send({
              status: false,
              message: '任务超时'
            })

            that.resetTask()
          })

        })

    console.log('捕获终端发来的发布任务消息')
  }
  
  // 开启服务
  runServer() {
    let that = this
    
    this.server = this.app.listen(this.port, function() {

      let host = that.server.address().address,
          port = that.server.address().port

      console.log('server run at ' + host + port)

    })

    console.log('开启服务')
  }
  
  // 重置任务状态
  resetTask() {

    this.status = 'waitTerminalTask'
    this.taskQueue.length > 0 && this.taskQueue.pop()
    this.taskResult = null
    this.taskStamp = null
    this.timeoutTimerIndex = 0

    console.log('重置任务状态')
  }
  
  // 开启任务步骤超时计时器
  runTimeoutTimer(type, fn, cb) {
    let that = this

    this.timeoutTimer = setInterval(function() {

      fn && fn()
      
      if (that.timeoutTimerIndex >= that.timeoutTimerLimit) {
        that.stopTimeoutTimer(function() {

          cb && cb()

          that.timeoutTimerIndex = 0

          console.log(type + ' 超时')
        })

        return
      }

      that.timeoutTimerIndex += that.timeoutTimerSpeed

      console.log(that.timeoutTimerIndex / 1000)

    }, this.timeoutTimerSpeed)


    console.log('开启任务步骤超时计时器')

  }

  // 关闭任务步骤超时计时器
  stopTimeoutTimer(cb) {
    clearInterval(this.timeoutTimer)

    cb && cb()
  }

  runLockTimeoutTimer(time) {
    let that = this

    time = time || this.lockTimeoutTimerLimit

    console.log('time')
    console.log(time)
    
    this.isLock = true

    this.stopLockTimeoutTimer()
    
    this.lockTimeoutTimer = setTimeout(function() {
      that.isLock = false
      that.stopLockTimeoutTimer()

      console.log('任务锁定已关闭')

    }, time)

    console.log('开启锁定计时器')

  }

  stopLockTimeoutTimer() {
    clearTimeout(this.lockTimeoutTimer)
  }

  // 日志功能
  log(text) {

    fs.appendFile('log.txt', new Date().toLocaleString() + '\n' + JSON.stringify(text) + '\n' + '============================================================================' + '\n', function(err) {
      if (err) {
        return console.log(err)
      }
    })
  }

  // methos
  // 判断是否为空对象
  isEmptyObject(obj) {
    return JSON.stringify(obj) === '{}' ? true: false
  }
  
  // 判断是否为函数
  isFunction(fun) {
    return Object.prototype.toString.call(fun) === '[object Function]' ? true: false
  }
  
  // 判断是否为对象
  isObject(obj) {
    return Object.prototype.toString.call(fun) === '[object Object]' ? true: false
  }

  // 判断是否为正整数
  isPositiveInteger(num) {
    return /^[0-9]+$/.test(num - 0) // 转换成数字
  }

  // 判断是否为空数据
  isEmptyArray(arr) {
    return arr.length === 0 ? true: false
  }
  
  // 判断task是否在task清单中
  isInTaskList(task) {
    let i   = 0,
        len = this.legalTaskList.length

    for (; i < len; i++) {
      if (this.legalTaskList[i] === task) {
        return true
      }
    }

    return false
  }
}

var c = new Credit()